package cn.com.okgo.callback;

import android.text.TextUtils;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.List;

import cn.com.okgo.cache.CacheMode;
import cn.com.okgo.cache.CacheUtils;
import cn.com.okgo.cache.TCache;
import cn.com.okgo.request.base.BaseRequest;
import cn.com.okgo.utils.Exceptions;
import cn.com.okgo.utils.HttpUtils;
import cn.com.okgo.utils.ThreadUtils;
import okhttp3.Callback;
import okhttp3.Response;
import okhttp3.ResponseBody;

/**
 * Created by luwenlong on 2017/8/29.
 */

public class Call<T> {

    /**
     * 站点列表
     */
    private List<String> baseUrls = new ArrayList<>();

    /**
     * 请求信息
     */
    private BaseRequest baseRequest;

    /**
     * 回调
     */
    private HttpCallback httpCallback;

    private okhttp3.Call call;

    /**
     * 返回code
     */
    private int httpCode;

    /**
     * 当前重试次数
     */
    private int currentRetryCount;

    /**
     * 缓存key 根据全路径获取
     */
    private String cacheKey;

    public void execute(BaseRequest baseRequest, HttpCallback<T> httpCallback) {
        this.baseRequest = baseRequest;
        this.httpCallback = httpCallback;
        String url = TextUtils.isEmpty(baseRequest.getBaseUrl()) ? baseRequest.getUrl() : baseRequest.getBaseUrl() + baseRequest.getUrl();
        cacheKey = CacheUtils.getKey(baseRequest);
        ResponseBody cacheResponseBody = getCache();
        if (null != baseRequest.getBaseUrls() && baseRequest.getBaseUrls().size() > 1) {
            if (baseRequest.getMaxRetry() > 1) {
                Exceptions.illegalArgument("暂时不支持 站点切换 与 重试 并存");
            }
            baseUrls.addAll(baseRequest.getBaseUrls());
            baseUrls.remove(baseRequest.getBaseUrl());
        }
        /**
         * 先返回缓存数据
         */
        if (baseRequest.getCacheMode() == CacheMode.FIRST_CACHE_THEN_REQUEST && null != cacheResponseBody) {
            onCacheResultCallback(cacheResponseBody);
        }
        call = baseRequest.generateCall();
        startExecute();
    }

    /**
     * 开始执行 okhttp execute
     */
    private void startExecute() {
        if (null == call) return;
        call.enqueue(new Callback() {
            @Override
            public void onFailure(okhttp3.Call call, IOException e) {
                retry(e, this);
            }

            @Override
            public void onResponse(okhttp3.Call call, Response response) throws IOException {
                httpCode = response.code();
                try {
                    if (httpCode >= 400) {
                        retry(Exceptions.ioException(response.toString()), this);
                    } else {
                        Object obj = httpCallback.parseNetworkResponse(response);
                        onSuccessResultCallback(response, obj);
                    }
                } catch (Exception e) {
                    retry(e, this);
                } finally {
                    if (response.body() != null) {
                        response.body().close();
                    }
                }
            }
        });
    }


    private ResponseBody getCache() {
        return TCache.getInstance().getResponseBody(cacheKey);
    }

    /**
     * 是否需要切换站点
     *
     * @return
     */
    private boolean needChangeService() {
        return HttpUtils.isNetworkConnected()
                && baseRequest.isNeedChangeService()
                && baseUrls.size() > 0
                && httpCode != 401
                && httpCode != 403;
    }

    /**
     * 重试
     * 1.优先站点切换
     * 2.自定义重试次数
     *
     * @param e
     * @param callback
     */
    private void retry(Exception e, Callback callback) {
        try {
            //切换站点
            if (needChangeService()) {
                baseRequest.baseUrl(baseUrls.get(0));
                okhttp3.Call newCall = baseRequest.generateCall();
                newCall.enqueue(callback);
                baseUrls.remove(0);
                onRetryResultCallback();
                cacheKey = CacheUtils.getKey(baseRequest);
            } else if (e instanceof SocketTimeoutException && currentRetryCount < baseRequest.getMaxRetry()) {
                currentRetryCount++;
                try {
                    Thread.sleep(baseRequest.getDelay() + (currentRetryCount - 1) * baseRequest.getIncreaseDelay());
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
                okhttp3.Call newCall = baseRequest.generateCall();
                newCall.enqueue(callback);
                onRetryResultCallback();
            } else if (baseRequest.getCacheMode() == CacheMode.REQUEST_FAILED_READ_CACHE && null != getCache()) {
                onCacheResultCallback(getCache());
            } else if (baseRequest.getCacheMode() == CacheMode.FIRST_CACHE_THEN_REQUEST) {
                //如果是先返回数据的模式，请求失败时，不再做任何的数据返回
            } else {
                onFailureResultCallback(e);
            }
        } catch (Exception e1) {
            onFailureResultCallback(e1);
        }
    }

    /**
     * 请求成功回调
     *
     * @param obj
     */
    private void onSuccessResultCallback(Response response, final Object obj) {
        if (httpCallback == null) return;
        ThreadUtils.dispatchToMainThread(new Runnable() {
            @Override
            public void run() {
                httpCallback.onSuccess(obj, baseRequest.getBaseUrl());
            }
        });
    }

    /**
     * 缓存回调
     *
     * @param responseBody
     */
    private void onCacheResultCallback(final ResponseBody responseBody) {
        if (httpCallback == null) return;
        ThreadUtils.dispatchToMainThread(new Runnable() {
            @Override
            public void run() {
                Object obj = null;
                try {
                    obj = httpCallback.parseCacheResponse(responseBody);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                httpCallback.onCache(obj, baseRequest.getBaseUrl());
            }
        });
    }

    /**
     * 请求失败回调
     *
     * @param e
     */
    private void onFailureResultCallback(final Exception e) {
        if (httpCallback == null) return;
        ThreadUtils.dispatchToMainThread(new Runnable() {
            @Override
            public void run() {
                httpCallback.onFailed(e, baseRequest.getBaseUrl());
            }
        });
    }


    /**
     * 站点切换/重试回调 调试使用
     */
    private void onRetryResultCallback() {
        if (httpCallback == null) return;
        ThreadUtils.dispatchToMainThread(new Runnable() {
            @Override
            public void run() {
                httpCallback.onRetry(baseRequest.getBaseUrl());
            }
        });
    }

}
